package cn.song.freight.controller;

import cn.hutool.json.JSONUtil;
import cn.song.freight.annotation.OperationLog;
import cn.song.freight.domain.dto.BaseIdDTO;
import cn.song.freight.domain.dto.FreightStrategyAddDTO;
import cn.song.freight.domain.dto.FreightStrategyEditDTO;
import cn.song.freight.domain.dto.FreightStrategyQueryDTO;
import cn.song.freight.domain.dto.StrategyCopyDTO;
import cn.song.freight.domain.entity.FreightStrategyDO;
import cn.song.freight.domain.result.R;
import cn.song.freight.enums.ModuleType;
import cn.song.freight.enums.OperationType;
import cn.song.freight.service.FreightStrategyService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.util.List;

/**
 * 运费策略管理控制器
 * <pre>
 * 该控制器管理物流系统的运费定价策略。
 * 提供运费策略的全面CRUD操作，包括创建、修改、删除和查询定价规则。
 *
 * 核心功能：
 * - 创建具有可定制定价模型的新运费策略
 * - 更新现有策略，同时保持数据一致性
 * - 删除策略，带有适当的验证和级联检查
 * - 灵活的策略查询，支持过滤和排序选项
 * - 获取详细策略信息，用于配置管理
 *
 * 支持的定价模型：
 * - 基本重量定价（首重 + 续重）
 * - 混合定价（重量定价 + 重量区间规则）
 * - 体积重考虑，可配置抛重比
 *
 * @author SongRenShuo
 * @version 3.0.0
 * @since 2025-08-23
 */
@Slf4j
@Api(tags = "运费策略管理")
@Validated
@RestController
@RequestMapping("/strategy")
@RequiredArgsConstructor
public class FreightStrategyController {

    /**
     * 运费策略服务，处理业务逻辑
     */
    private final FreightStrategyService freightStrategyService;

    /**
     * 创建新的运费策略
     * <pre>
     * 此端点使用指定的参数和规则创建新的运费定价策略。
     * 策略在创建后立即可用于运费计算和区域映射。
     *
     * @param strategyAddDTO 运费策略创建数据传输对象
     * @return 操作结果和创建的策略信息
     */
    @PostMapping("/add")
    @ApiOperation("创建新的运费策略")
    @OperationLog(
            moduleType = ModuleType.FREIGHT_STRATEGY,
            operationType = OperationType.INSERT,
            description = "新增运费策略",
            businessNameField = "name",
            businessIdField = "id"
    )
    public R<Boolean> addStrategy(@Valid @RequestBody FreightStrategyAddDTO strategyAddDTO) {
        log.info("创建新的运费策略: 名称={}, 定价模式={}",
                strategyAddDTO.getName(), strategyAddDTO.getPricingModel());

        Boolean result = freightStrategyService.add(strategyAddDTO);

        log.info("运费策略创建成功: 名称={}", strategyAddDTO.getName());
        return R.success(result);
    }

    /**
     * 删除现有的运费策略
     * <pre>
     * 此端点从系统中删除运费策略。删除前会验证
     * 该策略当前未被分配给任何仓库-物流公司组合
     * 且未与任何区域建立映射关系。
     *
     * @param deleteDTO 通用ID操作数据传输对象
     * @return 操作结果，指示删除成功或失败
     */
    @PostMapping("/delete")
    @ApiOperation("删除现有运费策略")
    @OperationLog(
            moduleType = ModuleType.FREIGHT_STRATEGY,
            operationType = OperationType.DELETE,
            description = "删除运费策略",
            businessIdField = "id"
    )
    public R<Boolean> deleteStrategy(@Valid @RequestBody BaseIdDTO deleteDTO) {
        log.info("删除运费策略: ID={}", deleteDTO.getId());

        Boolean result = freightStrategyService.delete(deleteDTO.getId());

        log.info("运费策略删除成功: ID={}", deleteDTO.getId());
        return R.success(result);
    }

    /**
     * 更新现有的运费策略
     * <pre>
     * 此端点修改现有运费策略的配置。
     * 更新以原子方式应用，确保相关区域映射
     * 和策略分配的数据一致性。
     *
     * @param strategyEditDTO 运费策略编辑数据传输对象，包含更新的策略信息
     * @return 操作结果，指示更新成功或失败
     */
    @PostMapping("/edit")
    @ApiOperation("更新现有运费策略")
    @OperationLog(
            moduleType = ModuleType.FREIGHT_STRATEGY,
            operationType = OperationType.UPDATE,
            description = "更新运费策略",
            businessNameField = "name",
            businessIdField = "id"
    )
    public R<Boolean> editStrategy(@Valid @RequestBody FreightStrategyEditDTO strategyEditDTO) {
        log.info("更新运费策略: ID={}, 名称={}", strategyEditDTO.getId(), strategyEditDTO.getName());

        Boolean result = freightStrategyService.edit(strategyEditDTO);

        log.info("运费策略更新成功: ID={}", strategyEditDTO.getId());
        return R.success(result);
    }

    /**
     * 使用过滤条件查询运费策略
     * <pre>
     * 此端点根据提供的查询参数检索运费策略列表。
     * 支持按名称、状态、计费模式等条件进行过滤。
     * 结果按策略优先级和创建时间排序。
     *
     * @param queryDTO 运费策略查询数据传输对象，包含过滤条件
     * @return 符合条件的运费策略列表
     */
    @PostMapping("/list")
    @ApiOperation("查询运费策略列表")
    public R<List<FreightStrategyDO>> listStrategies(@Valid @RequestBody FreightStrategyQueryDTO queryDTO) {
        log.info("查询运费策略，过滤条件: {}", JSONUtil.toJsonStr(queryDTO));

        List<FreightStrategyDO> result = freightStrategyService.list(queryDTO);

        log.info("找到 {} 个符合条件的运费策略", result.size());
        return R.success(result);
    }

    /**
     * 获取特定运费策略的详细信息
     * <pre>
     * 此端点检索单个运费策略的全面详细信息，
     * 包括相关的重量区间规则、区域映射和
     * 策略分配状态，用于详细视图和编辑操作。
     *
     * @param detailDTO 通用ID操作数据传输对象
     * @return 完整的策略信息和相关配置
     */
    @PostMapping("/detail")
    @ApiOperation("获取运费策略详细信息")
    public R<FreightStrategyDO> getStrategy(@Valid @RequestBody BaseIdDTO detailDTO) {
        log.info("获取运费策略详情: ID={}", detailDTO.getId());

        FreightStrategyDO result = freightStrategyService.detail(detailDTO.getId());

        log.info("获取运费策略详情成功: ID={}, 名称={}", detailDTO.getId(), result.getName());
        return R.success(result);
    }

    /**
     * 复制策略数据
     * <pre>
     * 此端点复制指定策略的所有配置信息（包括地区配置和重量区间规则），
     * 但不包括策略分配信息，用于前端编辑弹框的回显。
     *
     * @param copyDTO 复制策略数据传输对象
     * @return 复制操作是否成功
     */
    @PostMapping("/copy-data")
    @ApiOperation("复制策略数据")
    @OperationLog(
            moduleType = ModuleType.FREIGHT_STRATEGY,
            operationType = OperationType.INSERT,
            description = "复制运费策略数据",
            businessIdField = "id"
    )
    public R<Boolean> copyStrategyData(@Valid @RequestBody StrategyCopyDTO copyDTO) {
        log.info("复制策略数据: 原策略ID={}", copyDTO.getId());

        Boolean result = freightStrategyService.copyStrategyData(copyDTO);

        log.info("策略数据复制成功: 原策略ID={}", copyDTO.getId());
        return R.success(result);
    }
}
